﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Xaml;
using System.Xaml.Schema;

using agree.semi;

using alib;
using alib.Array;
using alib.Collections;
using alib.Debugging;
using alib.Dictionary;
using alib.String;
using alib.Enumerable;

namespace agree.mrs
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public abstract class MrsElement : ISemValue, IIdentList<Role>
	{
		public MrsElement(ISemantics sem, IReadOnlyCollection<Role> roles)
		{
			this.sem = sem;
			this.ix = -1;
			this.roles = roles != null ? roles as Role[] ?? roles.ToArray() : Collection<Role>.None;
		}
		public MrsElement(ISemantics sem)
			: this(sem, default(IReadOnlyCollection<Role>))
		{
		}
		public MrsElement(MrsElement to_copy)
			: this(to_copy.sem, to_copy.roles)
		{
		}

		public abstract String Name { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public readonly ISemantics sem;

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		Role[] roles;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int ix;

		public ISemValue this[String arg]
		{
			get
			{
				arg = arg.ToUpper();
				var _tmp = roles;
				for (int i = 0; i < _tmp.Length; i++)
					if (_tmp[i].name == arg)
						return _tmp[i].var;
				return null;
			}
			set
			{
				arg = arg.ToUpper();
				var _tmp = roles;
				for (int i = 0; i < _tmp.Length; i++)
					if (_tmp[i].name == arg)
					{
						_tmp[i].var = (Variable)value;
						return;
					}
				arr.Append(ref roles, new Role(arg, (Variable)value));
			}
		}

		/// <summary> i.e. for XAML </summary>
		public void Add(Role r) { this[r.name] = r.var; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Count { get { return roles.Length; } }

		public Role this[int index] { get { return roles[index]; } }

		public IEnumerable<_Role> _erex()
		{
			var rel = this as Relation;

			if (rel != null && rel.PRED != null)
				yield return new _Role("PRED", new Constant(sem, rel.PRED_no_rel));

			foreach (var role in roles)
				yield return new _Role(role);

			if (rel != null && rel.CARG != null)
				yield return new _Role("CARG", new Constant(sem, rel.CARG));
		}

		public IEnumerator<Role> GetEnumerator() { return roles.Enumerator(); }

		IEnumerator IEnumerable.GetEnumerator() { return roles.Enumerator(); }

		public void CopyTo(Array array, int index) { roles.CopyTo(array, index); }

		bool ICollection.IsSynchronized { get { return false; } }
		Object ICollection.SyncRoot { get { return this; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public virtual int MaxWidth { get { return this.Max(role => role.ToString().Length); } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public virtual String RelTypeName { get { return this.GetType().Name.ToLower(); } }

		public virtual IEnumerable<IProperty> Features
		{
			get
			{
				foreach (var role in roles)
					yield return new Property(sem, role.Name, role.Value);
			}
		}

		public IIdentity Trace { get { return sem; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class Hook : MrsElement
	{
		public Hook(Mrs mrs)
			: base(mrs)
		{
			base.ix = 0;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public override String Name { get { return "hook"; } }

		public Handle LTOP
		{
			get { return (Handle)this["LTOP"]; }
			set { this["LTOP"] = value; }
		}
		public Variable INDEX
		{
			get { return (Variable)this["INDEX"]; }
			set { this["INDEX"] = value; }
		}
		public Variable XARG
		{
			get { return (Variable)this["XARG"]; }
			set { this["XARG"] = value; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class Relation : MrsElement
	{
		public Relation(ISemantics sem)
			: base(sem)
		{
		}
		public Relation(ISemantics sem, String pred)
			: this(sem)
		{
			this.PRED = pred;
		}

		public override String Name { get { return "relation"; } }

		public Handle LBL
		{
			get { return this["LBL"] as Handle; }
			set { this["LBL"] = value; }
		}
		public String PRED { get; set; }
		public String PRED_no_rel { get { return PRED == null || PRED.Length == 0 || PRED[0] != '_' ? PRED : PRED.RemoveSuffix("_rel"); } }
		public String CARG { get; set; }
		//public String PRED { get { var r = this["PRED"]; return r != null ? r.Type : null; } }
		//public String CARG { get { var r = this["CARG"]; return r != null ? r.Type : null; } }

		public Variable ARG0
		{
			get { return (Variable)this["ARG0"]; }
			set { this["ARG0"] = value; }
		}
		public Variable ARG1
		{
			get { return (Variable)this["ARG1"]; }
			set { this["ARG1"] = value; }
		}
		public Variable ARG2
		{
			get { return (Variable)this["ARG2"]; }
			set { this["ARG2"] = value; }
		}

		public String TextFormat(BitArray var_disp_bits)
		{
			var lbl = this.LBL;
			return String.Format("{0}:{1}({2})",
				lbl != null ? lbl.FormatFirstUse(var_disp_bits) : "(no LBL)",
				PRED ?? "(no pred)",
				this.ExceptWhere(r => r.name == "LBL").Select(r => r.var).OfType<Variable>().Select(v => v.FormatFirstUse(var_disp_bits)).StringJoin(", "));
		}

		public override String ToString()
		{
			return TextFormat(null);
		}

		public override IEnumerable<IProperty> Features
		{
			get
			{
				if (PRED != null)
					yield return new Property(sem, "PRED", PRED);
				foreach (var ip in base.Features)
					yield return ip;
				if (CARG != null)
					yield return new Property(sem, "CARG", CARG);
			}
		}

	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class Qeq : MrsElement
	{
		public Qeq(Mrs mrs)
			: base(mrs)
		{
		}

		public override String Name { get { return "qeq"; } }

		public Handle HARG
		{
			get { return this["HARG"] as Handle; }
			set { this["HARG"] = value; }
		}
		public Handle LARG
		{
			get { return this["LARG"] as Handle; }
			set { this["LARG"] = value; }
		}

		public String TextFormat(BitArray var_disp_bits)
		{
			var harg = this.HARG;
			var larg = this.LARG;

			return String.Format("{0} qeq {1}",
				harg != null ? harg.FormatFirstUse(var_disp_bits) : "(null)",
				larg != null ? larg.FormatFirstUse(var_disp_bits) : "(null)");
		}

		public override String ToString()
		{
			return TextFormat(null);
		}
	};
}